/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.shef.speech_r25;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author samf
 */
public class WorkImpl {

    List<String> wordSeq;
    int prevPos;
    int currentPos;
    List<Integer> blankPos;
    List<String> answers;
    Map<Integer, String> blankAnswer;
    PrintStream out;
    
    public WorkImpl(String corpusFile, String answerFile, PrintStream out) {
        this.out = out;
        prevPos = currentPos = 0;
           
        wordSeq = readWordSeq(corpusFile);
        Segment.setWordSeq(wordSeq);

        answers = readList(answerFile);
        blankPos = findBlanks(wordSeq);
        blankAnswer = makeMap(blankPos, answers);
        if (answers.size() != blankPos.size()) {
            System.out.println("Mismatch blank and answers");
            System.out.println("Ans " + answers.size() + " != blanks "+ blankPos.size());
            
            System.exit(0);
        }

    }

    private List<String> readList(String fileName) {
        ArrayList<String> newList = new ArrayList<String>();
        try {
            BufferedReader r = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = r.readLine()) != null) {
                newList.add(line);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return newList;

    }

    private List<String> readWordSeq(String fileName) {
        ArrayList<String> newList = new ArrayList<String>();
        try {
            BufferedReader r = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = r.readLine()) != null) {
                String[] words = line.split(" ");
                newList.addAll(Arrays.asList(words));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return newList;

    }

    
    private List<Integer> findBlanks(List<String> words) {
        int count = 0;
        List<Integer> list = new ArrayList<Integer>();
        for (String word : wordSeq) {
            if (word.equals("<blank>")) {
                list.add(count);
            }
            ++count;
        }
        return list;

    }

    private Map<Integer, String> makeMap(List<Integer> blanks, List<String> answers) {
        LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
        Iterator<Integer> blankIt = blanks.iterator();
        Iterator<String> ansIt = answers.iterator();
        int blank;
        String ans;
        while (blankIt.hasNext()) {
            blank = blankIt.next();
            ans = ansIt.next();
            map.put(blank, ans);
        }
        return map;
    }


    int minimum(int a, int b, int c) {
        return Math.min(Math.min(a, b), c);
    }

    int computeLevenshteinDistance(String str1, String str2) {
        int[][] distance = new int[str1.length() + 1][str2.length() + 1];

        for (int i = 0; i <= str1.length(); i++) {
            distance[i][0] = i;
        }
        for (int j = 1; j <= str2.length(); j++) {
            distance[0][j] = j;
        }

        for (int i = 1; i <= str1.length(); i++) {
            for (int j = 1; j <= str2.length(); j++) {
                distance[i][j] = minimum(
                        distance[i - 1][j] + 1,
                        distance[i][j - 1] + 1,
                        distance[i - 1][j - 1] + ((str1.charAt(i - 1) == str2.charAt(j - 1)) ? 0 : 1));
            }
        }

        return distance[str1.length()][str2.length()];
    }

    float calcScore(Segment seg, String utt) {
        String segString = seg.toString();
        WER wer = new WER(segString, utt, " ");
        float werScore = wer.computeNumerator() / wer.computeDenominator();
        float distScore = (float) Math.abs(currentPos - seg.start) * 0.01f;
        // System.out.println(segString);
        //System.out.println(utt);
        //System.out.println(werScore + " "+ distScore);
        //return werScore;
        return werScore + distScore;
//return computeLevenshteinDistance(segString, utt);
    }

    Segment findBest(String utt) {
        float bestScore = 100000;
        Segment bestSeg = null;
        String[] uttWords = utt.split(" ");
        for (int i = 0; i < wordSeq.size() - uttWords.length + 1; ++i) {
            int start = i;
            int end = start + uttWords.length;
            Segment seg = new Segment(start, end);
            float score = calcScore(seg, utt);
            if (score < bestScore) {
                bestScore = score;
                bestSeg = seg;
            }

        }
        bestSeg.score = bestScore;
        prevPos = currentPos;

        if (bestSeg.end >= prevPos) {
            currentPos = bestSeg.end;
        }

        return bestSeg;
    }

    String getAnswer(Segment seg, int nearestBlank) {
        out.println("Get answer "+seg+" "+nearestBlank);
        if (seg.end <= nearestBlank) {
            int diff = nearestBlank - seg.end;
            seg.end = nearestBlank+1;
            seg.start += diff + 1;
        }

        String answer = blankAnswer.get(nearestBlank);
        return answer;
        //return seg.replace(nearestBlank, answer);
    }

    String respond(Segment seg) {
        out.println("Responding to '"+seg+"'");
        int nearestBlank = -999;
        int closestDist = 10000;
        for (int blank : blankPos) {
            //out.println("Considering " + blank);
            int dist = Math.abs(blank - seg.end);
            if (dist < closestDist) {
                closestDist = dist;
                nearestBlank = blank;
            }
        }
        out.println("nearestBlank = "+nearestBlank+" closest dist = "+closestDist);
        String answer = "null";
        if (closestDist < 2) {
            answer = getAnswer(seg, nearestBlank);
            out.println("answer is "+answer);
        }
        return answer;

    }

}

